package com.wgo.precise.client.ui.model;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;


import com.wgo.precise.client.ui.controller.ModelStatus;
import com.wgo.precise.client.ui.controller.RequirementPlugin;
import com.wgo.precise.client.ui.model.facade.IMenuItemsProvider;
import com.wgo.precise.client.ui.model.facade.IModelPersistable;
import com.wgo.precise.client.ui.model.facade.INodeChild;
import com.wgo.precise.client.ui.model.facade.INodeParent;
import com.wgo.precise.client.ui.model.facade.ITreeItem;
import com.wgo.precise.client.ui.model.facade.ModelSystem;
import com.wgo.precise.client.ui.view.util.ReflectionHelper;
import com.wgo.precise.client.ui.view.util.ViewerAction;
import com.wgo.precise.common.VO.Persistent;

/**
 * 
 * @author plheide
 *
 * @param <T>
 */
public class ClientSystemWrapper<T extends INodeChild> implements IModelPersistable, ITreeItem, INodeParent<ModelSystem<T>, T>, IMenuItemsProvider {

	private Persistent domainModelObject = null;
	private Image image = null;
	private String simpleName = null;
	private String text = null;
	private String description = null;
	private int sortingOrder = 999;
	private Method getChildrenMethod = null;
	private Method hasChildrenMethod = null;
	private Map<Persistent, T> children = new Hashtable<Persistent, T>();
    private List<ViewerAction> actionList = new ArrayList<ViewerAction>();

    
	public ClientSystemWrapper(Persistent domainModelObject, String text,
			int sortingOrder, String getChildrenMethodName, 
			String hasChildrenMethodName, ViewerAction action) {
		this(domainModelObject,
				PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER),
				"", text, "", sortingOrder, getChildrenMethodName,
				hasChildrenMethodName, action);
	}

	private ClientSystemWrapper(Persistent domainModelObject, Image image, 
			String simpleName, String text, String description, 
			int sortingOrder, String getChildrenMethodName, 
			String hasChildrenMethodName, ViewerAction action) {
		this.domainModelObject = domainModelObject;
		this.image = image;
		this.simpleName = simpleName;
		this.text = text;
		this.description = description;
		this.sortingOrder = sortingOrder;
		this.getChildrenMethod = ReflectionHelper.retrieveMethod("Could not retrieve getChildrenMethod", domainModelObject.getClass(), getChildrenMethodName);
		this.hasChildrenMethod = ReflectionHelper.retrieveMethod("Could not retrieve hasChildrenMethod", domainModelObject.getClass(), hasChildrenMethodName);
		actionList.add(action);
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.IModelPersistable#getDomainModelObject()
	 */
	public Persistent getDomainModelObject() {
		return domainModelObject;
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.IModelPersistable#getIdentifier()
	 */
	public Long getIdentifier() {
		return domainModelObject.getDbId();
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.IModelPersistable#getDomainObjectType()
	 */
	@SuppressWarnings("unchecked")
	public Class<? extends Persistent> getDomainObjectType() {
		return null != getDomainModelObject() ? (Class<? extends Persistent>) getDomainModelObject().getClass().getInterfaces()[0] : null;
	}

	/**
	 * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
	 */
	public Object getAdapter(@SuppressWarnings("unused") Class adapter) {
		return null;
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.ILabelProviderItem#getImage()
	 */
	public Image getImage() {
		return image;
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.ILabelProviderItem#getSimpleName()
	 */
	public String getSimpleName() {
		return simpleName;
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.ILabelProviderItem#getText()
	 */
	public String getText() {
		return text;
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.ILabelProviderItem#getToolTipText()
	 */
	public String getToolTipText() {
		return description;
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.ITreeItem#getSortingOrder()
	 */
	public int getSortingOrder() {
		return sortingOrder;
	}

	/**
	 * The system objects has no parent..
	 * 
	 * @see com.wgo.precise.client.ui.model.facade.INodeChild#getParent()
	 */
	public ModelSystem<T> getParent() {
		return null;
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.INodeChild#setParent(INodeParent)
	 */
	public void setParent(@SuppressWarnings("unused") ModelSystem<T> newParent) {
		// The system objects has no parent..
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.INodeParent#getChildren()
	 */
	@SuppressWarnings("unchecked")
	public Collection<T> getChildren() {
		Map<Persistent, T> newChildren = new Hashtable<Persistent, T>();
		synchronized (children) {
			for (Persistent childPersistent : ReflectionHelper.<Collection<Persistent>>invoke("Could not get system children.", domainModelObject, getChildrenMethod)) {
				T nodeChild = children.remove(childPersistent);
	        	if (null != nodeChild) {
	        		newChildren.put(childPersistent, nodeChild);
	        	} else {
	        		newChildren.put(childPersistent, (T)RequirementPlugin.getInstance().getSession().getWrapperFactory().getWrapper(childPersistent, this));
	        	}
	        }
	        for (T child : children.values()) {
	        	RequirementPlugin.log(new ModelStatus(IStatus.WARNING, getClass().getSimpleName() + "Inconsistent children collection: " + child + " not removed."));
	        }
	        children = newChildren;
		}
		return children.values();	
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.INodeParent#hasChildren()
	 */
	public final boolean hasChildren() {
//		boolean hasChildren = ReflectionHelper.<Boolean>invoke("Could not check if system has children.", domainModelObject, hasChildrenMethod).booleanValue() ;
//		boolean actuallyHasChildren =  ReflectionHelper.<Collection<Persistent>>invoke("Could not get system children.", domainModelObject, getChildrenMethod).size() > 0;
//		int actualChildren = ReflectionHelper.<Collection<Persistent>>invoke("Could not get system children.", domainModelObject, getChildrenMethod).size();
//		Assert.warningIfFalse(! hasChildren && 0 < children.size(), getClass().getSimpleName() 
//				+ ": Inconsistent childrens hierarchy. domainConcept.hasChildren(): " + hasChildren 
//				+ " Wrapper number of children: " + children.size()); // last check to ensure wysiwyg if save does work.. and triger children set update in gui..
//		Assert.warningIfFalse(hasChildren  == actuallyHasChildren, getClass().getSimpleName() 
//				+ ": Inconsistent childrens hierarchy. domainConcept.hasChildren(): " + hasChildren 
//				+ " Domain getChildren().size() > 0: " + actuallyHasChildren); // last check to ensure wysiwyg if save does work.. and triger children set update in gui..
//		Assert.warningIfFalse(children.size() != actualChildren, getClass().getSimpleName() 
//				+ ": Inconsistent childrens hierarchy. Actual number of children: " + actualChildren 
//				+ " Wrapper number of children: " + children.size()); // last check to ensure wysiwyg if save does work.. and triger children set update in gui..
//		return hasChildren && actuallyHasChildren;
		return getChildren().size() > 0;
	}

	public List<ViewerAction> getMenuItemActions() {
		return actionList;
	}

}
